디자이너에게 화면 구성을 받았다. 필요한 데이터는?
GraphQL 스키마는 SDL을 이용하여 정의됨.
npm install apollo-server graphql
schema.js
를 만들자
const typeDefs = gql`
"""
block comment like this!
"""
type SpaceCat {
"Line Comment like this"
name: String!
age: Int
missions: [Mission]
}
`
module.exports = typeDefs
아까 설치했던 apollo-server
를 사용하여 간단히 서빙: 서버의 역할은
const {ApolloServer} = require('apollo-server');
const typeDefs = require('./schema');
const server = new ApolloServer({typeDefs});
server.listen().then(() => {
console.log(`
🚀 Server is running!
🔉 Listening on port 4000
📭 Query at https://studio.apollographql.com/dev
`);
});
mocks 속성에 가짜 resolver를 넣을 수 있음. 아래 dev-studio 에 가보면 Query 엔트리가 기본적으로 입력되어있다.
https://studio.apollographql.com/
npm install graphql @apollo/client
아폴로 클라이언트 세팅
import {ApolloClient, InMemoryCache, ApolloProvider} from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache: new InMemoryCache()
});
필요한 페이지에서 gql 쿼리
import {gql} from '@apollo/client';
export const TRACKS = gql`
query getTracks {
tracksForHome {
id
title
thumbnail
length
modulesCount
author {
name
photo
}
}
}
`;
Best Practices
이런식으로 훅으로 사용가능
const Tracks = () => {
const {loading, error, data} = useQuery(TRACKS); // 이건 @apollo/client꺼
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return <Layout grid>{JSON.stringify(data)}</Layout>;
};
query 결과를 감싸는 component를 만드는 것도 좋은 방법
GraphQL clients 쿼리 -> HTTP POST/GET + query string -> GraphQL server AST parse
resolver의 역할: 데이터 소스에서 가져온 데이터를 각 필드별로 어떤식으로 해결(데이터 반환) 할지 결정 데이터 소스는 다음이 될 수 있다.
Track마다 Author 정보를 가져와야 한다면? Track 배열을 가져오고 거기다 N번만큼 REST 요청을 엄청 많이 해야하나?
이처럼 단순히 fetch하는 방법으로는 제약이 많다.. 그래서 Cache를 잘 활용하고
RESTDataSource
라는 것을 구현해서 이용한다.
npm install apollo-datasource-rest
// src/datasources/track-api.js
const {RESTDataSource} = require('apollo-datasource-rest');
class TrackAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://odyssey-lift-off-rest-api.herokuapp.com/';
}
getTracksForHome() {
return this.get('tracks');
}
getAuthor(authorId) {
return this.get(`author/${authorId}`);
}
}
module.exports = TrackAPI;
인자 4가지: parent
, args
, context
, and info
사용하지 않는 인자는 컨벤션으로 _
, __
이런식으로 마킹한다.
Track의 Author 정보가 항상 필요하진 않다. 그래서 Track을 모두 가져오는 resolver에는 Author가져오는 걸 넣으면 안된다.
const resolvers = {
Query: {
tracksForHome: (_, __, {dataSources}) => {
return dataSources.trackAPI.getTracksForHome();
}
},
Track: {
// 여기서 parent는 tracksForHome에서 받은 Track 정보를 갖는다.
// 가져온 각 Track에 대해 Track resolver가 실행된다.
author: (parent, _, {dataSources}) => {
}
}
}
module.exports = resolvers;
Best Practice
resolvers and data sources를 만들 때 resolver functions의 깊이를 최대한 얕게 하는게 변화/유지보수에 좋다. 그리고 읽기에도 편하고 리팩토링시에도 용이하다.
🤔 context의 dataSources는 어떻게 RESTDataSource알고있을까? 아직 그부분을 하지 않았다!
이 모든건 Apollo Server에서 연결된다.
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
trackAPI: new TrackAPI()
};
}
});
Error...
Query에 인자 추가하여 받기
type Query {
"Query to get tracks array for the homepage grid"
tracksForHome: [Track!]!
track(id: ID!): Track
}
각 트랙에서 modules를 가져올 때... track 의 resolver에서 가져와서 처리할 건가? 아니지...
Apollo Studio에서 schema 변경점 확인 가능...
Client쪽에서 변수 넘기기 아래처럼 할 수 있다.
const {loading, error, data} = useQuery(GET_TRACK, {variables: {trackId}});
type Mutation {
addSpaceCat(name: String!): SpaceCat
}
만약 두 가지 오브젝트를 변형하는 mutation의 경우, 둘다 반환해준다.
그리고 code
, success
, message
처럼 부가 정보고 가져온다.
type Mutation {
incrementTrackViews(id: ID!): IncrementTrackViewsResponse
}
type IncrementTrackViewsResponse {
code: Int!
success: Boolean!
message: String!
track: Track
}
resolver 함수명은 schema 필드명과 같아야한다. 이런 경우 resolver에서 서버 응답에 따라 다른 값을 줘야하므로 비동기로 받은 후 result Object를 따로 생성해줘야한다.
쿼리 호출 방법
mutation IncrementTrackViewsMutation($incrementTrackViewsId: ID!) {
incrementTrackViews(id: $incrementTrackViewsId) {
code
success
message
track {
id
numberOfViews
}
}
}
클라이언트에서 (웹프론트) useMutation
을 이용한다.
const [incrementTrackViews, {loading, error, data}] = useMutation(INCREMENT_TRACK_VIEWS, { variables: {incrementTrackViewsId: id} });
Apollo Studio에서 Schema Registry 사용하기: Schema에 대한 Version Control을 제공 subgraph로 나뉘었을 때 충돌해결도 가능하다.
Apollo Studio에서 deployed graph로 만든 뒤, apollo server를 등록. 이러면 실행시마다 기록됨
APOLLO_KEY=service:xxxxx
APOLLO_GRAPH_ID=xxxxxx
APOLLO_GRAPH_VARIANT=current
APOLLO_SCHEMA_REPORTING=true
introspection을 공개하지 않고도, apollo studio를 통해 query를 실행해볼 수 있다.
서버, 클라이언트 따로 호스팅. 클라이언트에서 apollo server 설정할 때 미리 호스팅한 서버로 주소를 변경해줘야한다.
만약 더이상 특정 필드를 쓰지 못하게 된다면?
@deprecated
schema directive로 명시해준다. 보통 schema directive는 reason
을 인자로 받는다.
"The track's approximate length to complete, in seconds"length: Int @deprecated(reason: "Use durationInSeconds")